package ph.com.gs3.formalistics.service.managers;

import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;

import ph.com.gs3.formalistics.global.utilities.logging.FLLogger;
import ph.com.gs3.formalistics.global.utilities.logging.FLLogger.LogType;
import ph.com.gs3.formalistics.model.communicators.FormCommunicatorModule;
import ph.com.gs3.formalistics.model.communicators.HttpCommunicatorModule.CommunicatorException;
import ph.com.gs3.formalistics.model.dao.FormsDAO;
import ph.com.gs3.formalistics.model.dao.UserAccountsDAO;
import ph.com.gs3.formalistics.model.dao.SQLiteDataAccessObject.InsertFailedException;
import ph.com.gs3.formalistics.model.valueobjects.application.APIResponse;
import ph.com.gs3.formalistics.model.valueobjects.application.APIResponse.APIInvalidResponseException;
import ph.com.gs3.formalistics.model.valueobjects.business.User;
import ph.com.gs3.formalistics.model.valueobjects.business.form.Form;
import ph.com.gs3.formalistics.model.valueobjects.business.form.Form.InvalidFormException;
import ph.com.gs3.formalistics.model.valueobjects.business.form.FormField.InvalidFormFieldException;
import android.database.SQLException;

public class FormManager {

	public static final String TAG = FormManager.class.getSimpleName();

	private UserAccountsDAO userAccountsDAO;
	private FormsDAO formsDAO;
	private FormCommunicatorModule formCommModule;

	public FormManager(UserAccountsDAO userAccountsDAO, FormsDAO formsDAO,
	        FormCommunicatorModule formCommModule) {
		this.userAccountsDAO = userAccountsDAO;
		this.formsDAO = formsDAO;
		this.formCommModule = formCommModule;
	}

	public List<Form> updateForms() throws FormsUpdateFailedException {

		User currentUser;
		APIResponse downloadResponse;

		// get updates starting from today's date
		try {
			List<Form> forms = new ArrayList<Form>();
			userAccountsDAO.open();
			formsDAO.open();

			currentUser = userAccountsDAO.getActiveUserAccount();

			if (currentUser == null) {
				// code level error
				throw new IllegalStateException("Cannot update forms if there is no active user");
			}

			try {
				// if there are forms existing in the database, add a filter by last
				// update, otherwise, don't put filter
				if (formsDAO.getFormCount(formCommModule.getServer()) > 0) {
					String lastFormsUpdate = currentUser.getLastAllFormsUpdate();

					FLLogger.d(TAG, "Updating forms starting from: " + lastFormsUpdate);

					downloadResponse = formCommModule.updateAllForms(lastFormsUpdate);
				} else {
					downloadResponse = formCommModule.updateAllForms(null);
				}

			} catch (CommunicatorException e) {
				throw new FormsUpdateFailedException(e);
			}

			if (downloadResponse.isOperationSuccessful()) {

				String resultsRaw = downloadResponse.getResults();
				// throws JSONException
				JSONArray results = new JSONArray(resultsRaw);

				for (int i = 0; i < results.length(); i++) {

					try {
						Form form = Form.createFromJSON(results.getJSONObject(i),
						        formCommModule.getServer());

						// throws SQLException, InvalidFormFieldException
						forms.add(formsDAO.saveForm(form));
						
						FLLogger.d(TAG, "Updated/Saved form: " + form.getName());
					} catch (InvalidFormException e) {
						FLLogger.e(TAG, "InvalidFormException: " + e.getMessage()
						        + " \nRaw JSON response: " + results.getJSONObject(i));
						e.printStackTrace();
					} catch (SQLException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (InvalidFormFieldException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

				}

				// update the last form update
				String server = currentUser.getAccountServer();
				int webId = currentUser.getWebId();

				// throws SaveFailedException
				userAccountsDAO.setLastAllFormsUpdate(server, webId,
				        downloadResponse.getServerDate());

				FLLogger.d(TAG, forms.size() + " updated forms");
				
				return forms;

			} else {
				FLLogger.log(TAG, LogType.ERROR,
				        "Error downloading forms: " + downloadResponse.getErrorMessage());
				throw new FormsUpdateFailedException(new CommunicatorException(
				        downloadResponse.getErrorMessage()));
			}

		} catch (APIInvalidResponseException e) {
			throw new FormsUpdateFailedException(e);
		} catch (JSONException e) {
			throw new FormsUpdateFailedException(e);
		} catch (InsertFailedException e) {
			throw new FormsUpdateFailedException(e);
		} finally {
			formsDAO.close();
			userAccountsDAO.close();
		}

	}

	public static class FormsUpdateFailedException extends Exception {

		private static final long serialVersionUID = 7070178684027422676L;

		public FormsUpdateFailedException(String message) {
			super(message);
		}

		public FormsUpdateFailedException(Throwable throwable) {
			super(throwable);
		}

		public FormsUpdateFailedException(String message, Throwable throwable) {
			super(message, throwable);
		}

	}

}
